/* app_lwip_udp_server.c */

#include "app_inc.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define UDP_ECHO_PORT 7 /* UDP服务熟知端口. */

/*******************************************************************************
 * Variables
 ******************************************************************************/

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
/* 这里作为参数传入函数的函数指针, 其类型都是由lwip预先定义好了,
 * 用户可以遵循lwip定义好的流程想方设法拿到消息数据或者发送数据.
 */
static void app_lwip_udp_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p,
                                   struct ip_addr *addr, u16_t port);

/*******************************************************************************
 * Code
 ******************************************************************************/

static char *st_buff = "we got a udp msg.\r\n";

void  app_lwip_udp_server_init(void)
{
    /* 新建控制块 */
    struct udp_pcb * upcb = udp_new();

    /* 将pcb控制块绑定一个端口, 已接受客户端的连接.
     * 使用IP_ADDR_ANY作为pcb控制块的ip地址, 意味着这个pcb可以给任何本地的接口使用.
     */
    udp_bind(upcb, IP_ADDR_ANY, UDP_ECHO_PORT);

    /* 向lwip注册回调函数,
     * 当server建立好一个连接后, lwip会自动调用用户指定的app_lwip_server_accept()函数处理消息数据. */
    udp_recv(upcb, app_lwip_udp_callback, st_buff); /* 传入st_buff参数, 此参数将作为arg传入回调函数. */
}

/* 自己创建udp数据包 */
static void app_lwip_udp_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p,
                                   struct ip_addr *addr, u16_t port)
{
    /* 把收到的参数打印出来.*/
    if (arg) /* 此时的arg就是之前注册回调函数时传入的st_buff. */
    {
        printf("%s\r\n", (char *)arg);
    }

    /* 把收到的pbuf链表释放掉, 里面的消息不要了, 后面我要重新创建pbuf链消息. */
    pbuf_free(p);

    /* 开始构建自定义的udp数据包. */
    const char *hello_str = "hello, this is a reply.\r\n";
    struct pbuf *q = pbuf_alloc(PBUF_TRANSPORT, strlen(hello_str)+1, PBUF_RAM);
    if (!q)
    {
        printf("pbuf_alloc() error.\r\n");
        return;
    }
    memset(q->payload, 0, q->len);
    memcpy(q->payload, hello_str, strlen(hello_str));

    /* 发送pbuf消息. */
    udp_sendto(upcb, q, addr, port);
    pbuf_free(q);


}

#if 0 /* 回显udp数据包. */
static void app_lwip_udp_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p,
                                   struct ip_addr *addr, u16_t port)
{
    if (p != NULL)
    {
        /* 提取接收数据. */
        /* 提取其它udp server发来的udp包. */
        char * data_recv = (char *)mem_calloc(p->tot_len + 1, 1); /* 多的1个字节存放的是'\0', 作为字符串的结尾符. */
        if (NULL == data_recv)
        {
            printf("[Client] mem_calloc error, len = %u\r\n", p->tot_len);
        }
        else
        {
            /* 把pbuf链中的数据内容复制到一块连续的内存区中. */
            struct pbuf * q = p;
            int index = 0;

            while(q != NULL)
            {
                memcpy(&data_recv[index],q->payload,q->len);
                index += q->len;
                q = q->next;
            }

            /* 再次插入用户程序在tcp server收到数据有的处理过程. */
            printf("[Client] Get MSG: %s\r\n", data_recv);

            /* 释放内存空间 */
            mem_free(data_recv);
            data_recv = NULL;
        }

        /* 回复数据包. */
        udp_sendto(upcb, p, addr, port); /* 将收到的udp报文原封不动发回发送主机. */
        pbuf_free(p); /* 至此, 报文消息pbuf链表彻底不用了, 可以回收. */

    }
}
#endif

/* EOF. */

